package com.chehejia.saos.resubmitredis.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;

/**
 * 防止表单重复提交拦截器
 *
 * @author wangyongkun
 * @create 2021/3/2
 */
@Aspect
@Component
public class RepeatSubmitAspect {
    private static final Logger log = LoggerFactory.getLogger(RepeatSubmitAspect.class);
    public static final String DUPLICATE_TOKEN_KEY = "duplicate_token_key";
    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private LockUtil lockUtil;


    @Pointcut("@annotation(com.chehejia.saos.resubmitredis.config.NoRepeatSubmit)")
    public void doNoRepeatSubmit() {
    }

    @Before(value = "doNoRepeatSubmit() && @annotation(token)")
    public void before(final JoinPoint joinPoint, NoRepeatSubmit token) {
        if (token != null) {
            boolean enable = token.enable();
            if (enable) {
                String key = getDuplicateTokenKey(joinPoint, token);
                boolean lock = lockUtil.lock(key, key, token.timeout());
                if (!lock) {
                    throw new RepeatSubmitException("请求重复，请稍后再试！");
                } else {
                    log.info("正常,放行!");
                }
            }

        }
    }


    @AfterReturning(value = "doNoRepeatSubmit() && @annotation(token)", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, NoRepeatSubmit token,Object result) {
        if (token != null) {
            boolean enable = token.enable();
            if (enable) {
                String key = getDuplicateTokenKey(joinPoint, token);
                boolean unlock = lockUtil.unlock(key, key);
                if (unlock) {
                    log.info("锁删除成功!");
                } else {
                    log.info("不存在,放行");
                }
            }
        }
    }

    /**
     * 异常
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "doNoRepeatSubmit()&& @annotation(token)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e, NoRepeatSubmit token) {
        if (null != token && e instanceof RepeatSubmitException == false) {
            //处理处理重复提交本身之外的异常
            boolean enable = token.enable();
            //获得方法名称
            if (enable) {
                String key = getDuplicateTokenKey(joinPoint, token);
                boolean unlock = lockUtil.unlock(key, key);
                if (unlock) {
                    log.info("锁删除成功!");
                } else {
                    log.info("不存在,放行!");
                }
            }
        }
    }

    /**
     * 获取重复提交key
     *
     * @param joinPoint
     * @return
     */
    public String getDuplicateTokenKey(JoinPoint joinPoint, NoRepeatSubmit token) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String methodName = joinPoint.getSignature().getName();
        String keyGenerator = token.keyGenerator();
        String args = toJsonString(joinPoint.getArgs());
        StringBuilder key = new StringBuilder(DUPLICATE_TOKEN_KEY);
        key.append(",").append(args);
        if (KeyGeneratorEnum.H.getFlag().equals(keyGenerator) || KeyGeneratorEnum.M.getFlag().equals(keyGenerator)) {
            key.append(",").append(methodName);
        }
        if (KeyGeneratorEnum.H.getFlag().equals(keyGenerator)) {
            String requestURI = request.getRequestURI();
            if (requestURI != null) {
                key.append(",").append(requestURI);
            }
        }
        String md5 = jdkMD5(key.toString());
        return token.redisKey() + ":" + md5;
    }

    private String jdkMD5(String src) {
        String res = null;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            byte[] mdBytes = messageDigest.digest(src.getBytes());
            res = DatatypeConverter.printHexBinary(mdBytes);
        } catch (Exception e) {
            log.error("", e);
        }
        return res;
    }


    public String toJsonString(Object value) {
        try {
            return objectMapper.writeValueAsString(value);
        } catch (JsonProcessingException e) {
            log.error("JsonService序列化异常,异常原因", e);
            throw new BizException("JsonService序列化异常,原因" + e.toString());
        }
    }
}
